<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <!-- 1.缓存 DOM 引用 -->
    <script>
        var render = (function () {
            // get DOM 
            var domCache = document.querySelector("dom");

            return function () {
                // do something...
                domCache.style.width = '100px';
                domCache.style.height = '100px';
                // ....
            }
        })();
    </script>
    <!-- 2.缓存 DOM 的属性  -->
    <script>
        // bad
        var dom = document.querySelector("dom");
        var step = 1;

        var timer = window.setInterval(function () {
            var left = parseInt(dom.style.left);
            if (left >= 200) {
                clearInterval(timer);
            }
            dom.style.left = (left + 1) + 'px';
        }, 1000 / 60);

        // good
        var dom = document.querySelector("dom");
        var step = 1;
        var left = parseInt(dom.style.left);

        var timer = window.setInterval(function () {
            if (left >= 200) {
                clearInterval(timer);
            }
            left++;
            dom.style.left = left + 'px';
        }, 1000 / 60);
    </script>

    <!-- 3.减少 DOM 的写操作 : 记录上次结果与现有结果 Diff, 如有变化才进行写操作，去除不必要的写操作。-->
    <script>
        var dom = document.querySelector('#dom');
        var lastVal = null;
        var currVal = null;
        if (lastVal !== currVal) {
            dom.style.someAttr = currVal;
        }
    </script>


    <!-- 
        4.避免循环操作 DOM 元素
          合并多次写操作
    -->
    <script>
        // bad
        for (var i = 0; length < 100; i++) {
            // 一次 get，一次 set
            document.getElementById('text').innerHTML += `text${i}`
        }
        // better
        var html = '';

        for (var i = 0; length < 100; i++) {
            html += `text${i}`
        }

        document.getElementById('text').innerHTML = html;
    </script>

    <!-- 
        5.回流（reflow）与重绘（repaint） 如果了解过浏览器的渲染原理，我们知道，重绘和回流的性能消耗是非常严重的，
        破坏用户体验，造成UI卡顿。回流也叫重排，回流一定会引起重绘，重绘不一定会触发回流。触发浏览器回流与重绘的条件有:
         1.添加或者删除可见的DOM元素
         2.元素位置改变
         3.元素尺寸改变
         4.元素内容改变
         5.页面渲染初始化
         6.浏览器窗口尺寸改变，字体大小改变，页面滚动 
        我们的优化思路是减少甚至避免触发浏览器产生回流与重绘。 
        避免一些引起浏览器回流的属性 当获取一些属性值时，浏览器为取得正确的值也会发生重排，这些属性包括：
          1.Element:
                offsetTop、offsetLeft、offsetWidth、offsetHeight
                scrollTop、scrollLeft、scrollWidth、scrollHeight
                clientTop、clientLeft、clientWidth、clientHeight
          2. window:
                getComputedStyle()
                scrollBy()、scrollTo()、scrollX、scrollY
                webkitConvertPointFromNodeToPage()、webkitConvertPointFromPageToNode()  
    -->
    <!-- 
         7.在display:none的元素上进行操作
         如果 DOM 元素上需要进行很多操作，可以让该DOM 元素从 DOM 树中"离线"——display:none，
         等操作完毕后再”上线“取消display:none。这样能去除在操作期间引发的回流与重绘。 
    -->


    <!-- 
        8.浏览器优化
        重排和重绘很容易被引起，而且重排的花销也不小，如果每句 JavaScript 操作都去重排重绘的话，
        浏览器可能就会受不了。所以很多浏览器都会优化这些操作，浏览器会维护一个队列，
        把所有会引起重排、重绘的操作放入这个队列，等队列中的操作到了一定的数量或者到了一定的时间间隔，
        浏览器就会 flush 队列，进行一个批处理。这样就会让多次的重排、重绘变成一次重排重绘。
    -->
    <script>
        var dom = document.querySelector("#dom");

        // 触发两次 layout
        var newWidth = dom.offsetWidth + 10; // Read  
        aDiv.style.width = newWidth + 'px'; // Write  
        var newHeight = dom.offsetHeight + 10; // Read  
        aDiv.style.height = newHeight + 'px'; // Write

        // 只触发一次 layout
        var newWidth = dom.offsetWidth + 10; // Read  
        var newHeight = dom.offsetHeight + 10; // Read  
        aDiv.style.width = newWidth + 'px'; // Write  
        aDiv.style.height = newHeight + 'px'; // Write
    </script>

    <!-- 9.通过样式去改变元素样式  -->
    <script>
        // bad
        var dom = document.getElementById('dom');
        dom.style.color = '#FFF';
        dom.style.fontSize = '12px';
        dom.style.width = '200px';
    </script>
    <!-- 
         上述例子每次修改 style 属性后都会触发元素的重绘，如果修改了的属性涉及大小和位置，
         将会导致回流。所以我们应当尽量避免多次为一个元素设置 style 属性，
         应当通过给其添加新的 CSS 类，来修改其样式。 
    -->

    <!--better-->
    <style>
        .my-style {
            color: #FFF;
            font-size: 12px;
            width: 200px;
        }
    </style>
    <script>
        var dom = document.getElementById('dom');
        dom.classList.add('my-style');
    </script>

    <!-- 10.cssText  同上文优化思路，用cssText也可达到类似目的。-->
    <script>
        var dom = document.getElementById('dom');
        dom.style.cssText = 'color: #FFF;font-size: 12px;width: 200px;'
    </script>

    <!-- 11.使用定位让元素脱离文档流 : 使用定位让元素脱离文档流，引发回流重绘的 DOM 树范围被大大缩小。-->
    <style>
        .selector {
            position: fixed;
            /* or */
            position: absolute;
        }
    </style>

    <!-- 12.使用 transform 与 opacity  :transform 和 opacity 保证了元素属性的变化不影响文档流、也不受文档流影响，并且不会造成重绘。-->
    <!-- 13.避免复杂的 CSS 选择器以及 calc  : 复杂的 CSS 选择器会导致浏览器作大量的计算，我们应当避免-->
    <style>
        .box:nth-last-child(-n+1) .title {
            /* styles */
        }
    </style>

    <!-- 
        14.避免动画中使用使用高开销的CSS属性;
        CSS 有些属性存性能问题，使用它们会导致浏览器进行大量计算，特别是在 animation 中，我们应该谨慎使用，如:
          box-shaow
          background-image:
          filter
          border-radius
          transforms
          filters
    -->
    <!-- 15.使用 flexbox 布局替代 浮动布局 -->
</body>

</html>